#include "index.hpp"
#include "util.hpp"
#include <algorithm>
#include <regex>
#include <jsoncpp/json/json.h>


namespace ns_searcher{
  struct InvertedElemPrint{
    uint64_t doc_id;
    int weight;
    std::vector<std::string> words;
    InvertedElemPrint(): doc_id(0), weight(0){}
  };

  class Searcher{
    private:
      ns_index::Index *index;
    public:
      Searcher(){
      }
      ~Searcher(){
      }
    public:
      void InitSearcher(const std::string &input);
      void Search(const std::string& query, std::string *json_string);
  };

  std::string GetDesc(const std::string &html_content, const std::string &word){
    //找到word再html_contemnt 中首次出现，然后从前往后找50字节(如果没有，则从begin 开始), 往后找100 字节(如果没有，到end就可以了)
    //检索中文不能按照字节数目来截取，因为一个中文汉字存储超过一个字节，可能会发生截断产生不可显字符
    //截取这部分内容 
    const std::size_t prev_step = 50;
    const std::size_t next_step = 100;
    //1.找到首次出现 
    auto iter = std::search(html_content.begin(), html_content.end(),word.begin(), word.end(), [](int x, int y){return std::tolower(x) == std::tolower(y);});
    if (iter == html_content.end()){
      return "NONE1";
    }
    std::size_t pos = std::distance(html_content.begin(), iter);
    //2.获取start， end 
    std::size_t start = 0;
    std::size_t end = html_content.size(); //01234
    if (pos > prev_step){
      start = pos - prev_step;
    }
    if (pos + next_step < end){
      end = pos + next_step;
    }
    //3.截取字串,return 
    if (start > end){
      return "NONE2";
    }
    std::string ret =  html_content.substr(start, end - start);
    ret += "...";
    return ret;
    //return html_content.substr(start, end - start);
  }
  void Searcher::InitSearcher(const std::string &input){
      //1. 获取或者创建index对象
      index = ns_index::Index::GetInstance();
      LOG(NORMAL, "获取Index单例成功");
      //2. 根据index对象建立索引 
      index->BuildIndex(input);
      //std::cout << "建立正排和倒排索引成功..." << std::endl;
      LOG(NORMAL, "建立索引成功");
  }
  //query:搜索关键字 json_string: 返回给用户浏览器的搜索结果
  void Searcher::Search(const std::string& query, std::string *json_string){
    //1. [分词], 对query进行searcher的要求进行分词
    std::vector<std::string> words;
    ns_util::JiebaUtil::CutString(query, &words); 
    //2. [触发], 根据分词的词进行index查找
    //std::vector<ns_index::InvertedElem> inverted_list_all;      //将倒排结点全部取出放在(for debug 待优化)
    std::vector<InvertedElemPrint> inverted_list_all;
    std::unordered_map<uint64_t, InvertedElemPrint> tokens_map; //将所有倒排索引进行去重，权值相加

    for (std::string word : words){
      boost::to_lower(word);  //搜索时候关键字需要转成小写的
      ns_index::InvertedList* inverted_list = index->GetInvertedList(word);
      if (nullptr == inverted_list){
        continue;
      }
      //将所有倒排拉链中的doc_id 倒入一个vector中(会重复，待优化)
      //inverted_list_all.insert(inverted_list_all.end(), inverted_list->begin(), inverted_list->end());
      
      //()将倒排拉链中的元素进行去重
      for (const auto& elem :  *inverted_list){
        auto& item = tokens_map[elem.doc_id];
        item.doc_id = elem.doc_id;
        //item.weight += elem.weight;
        //但从存在来计算权值
        item.weight++;
        item.words.push_back(elem.word);
      }
    }

    for (const auto& item : tokens_map){
      inverted_list_all.push_back(item.second);
    }
    //3. [合并排序], 汇总查找结果，按照相关性(weight)降序排序
    //for debug
    //std::sort(inverted_list_all.begin(), inverted_list_all.end(), 
    //    [](const ns_index::InvertedElem& e1, const ns_index::InvertedElem& e2){return e1.weight > e2.weight;});
    std::sort(inverted_list_all.begin(), inverted_list_all.end(), 
        [](const InvertedElemPrint& e1, const InvertedElemPrint& e2){
        return e1.weight > e2.weight;});
    //4. [构建]: 根据查找出来的结果，构建json串 --jsoncpp
    Json::Value root;
    for (auto& item : inverted_list_all){
      ns_index::DocInfo* doc = index->GetForwardIndex(item.doc_id);
      if (nullptr == doc){
        continue;
      }
      Json::Value elem;
      elem["title"] = doc->title;
      elem["desc"] = GetDesc(doc->content, item.words[0]);   //摘要是一部分, 之后处理
      elem["url"] = doc->url;
      //for debug
      elem["id"] = (int)item.doc_id;
      elem["weight"] = item.weight;
      root.append(elem);
    }
    Json::StyledWriter writer;
    *json_string = writer.write(root);
  }
}
